use crate::controller::error::Error;
use crate::{dto, mq, service::ServiceSet};
use std::sync::Arc;
use wanmesh_common::error::ResponseError;

pub struct EndpointPeerWireguardConfigController {
    service_set: Arc<ServiceSet>,
    event_handler: EventHandler,
}

impl EndpointPeerWireguardConfigController {
    pub fn new(service_set: Arc<ServiceSet>, mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self {
            service_set,
            event_handler: EventHandler::new(mq_client_set),
        }
    }
    pub async fn add(
        &self,
        request: &dto::endpoint_peer_wireguard_config::AddRequest,
    ) -> Result<(), Error> {
        let endpoint = self
            .service_set
            .endpoint_peer_manager
            .get_endpoint(request.endpoint_peer_id.as_str())
            .await?;

        self.service_set
            .endpoint_peer_wireguard_config_manager
            .add(request.endpoint_peer_id.as_str(), &request.inner)
            .await?;

        self.event_handler.spawn_handle_setup_event(
            endpoint.inner.node_id.as_str(),
            dto::event::endpoint::peer::wireguard_config::SetupEventPayload {
                endpoint_id: endpoint.endpoint_id.clone(),
                endpoint_peer_id: request.endpoint_peer_id.clone(),
            },
        );

        Ok(())
    }
    pub async fn get(
        &self,
        request: &dto::endpoint_peer_wireguard_config::GetRequest,
    ) -> Result<dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig, Error> {
        let config = self.try_get(request).await?.ok_or(Error::ResponseError(
            ResponseError::endpoint_peer_wireguard_config_not_found(
                request.endpoint_peer_id.as_str(),
            ),
        ))?;

        Ok(config)
    }

    pub async fn try_get(
        &self,
        request: &dto::endpoint_peer_wireguard_config::GetRequest,
    ) -> Result<Option<dto::core::endpoint_peer_wireguard_config::EndpointPeerWireguardConfig>, Error>
    {
        let data = self
            .service_set
            .endpoint_peer_wireguard_config_manager
            .try_get(request.endpoint_peer_id.as_str())
            .await?;

        Ok(data)
    }

    pub async fn update(
        &self,
        request: &dto::endpoint_peer_wireguard_config::UpdateRequest,
    ) -> Result<(), Error> {
        if request.params == Default::default() {
            return Ok(());
        }

        let endpoint = self
            .service_set
            .endpoint_peer_manager
            .get_endpoint(request.endpoint_peer_id.as_str().into())
            .await?;

        self.service_set
            .endpoint_peer_wireguard_config_manager
            .update(request.endpoint_peer_id.as_str(), &request.params)
            .await?;

        self.event_handler.spawn_handle_setup_event(
            endpoint.inner.node_id.as_str(),
            dto::event::endpoint::peer::wireguard_config::SetupEventPayload {
                endpoint_id: endpoint.endpoint_id.clone(),
                endpoint_peer_id: request.endpoint_peer_id.clone(),
            },
        );

        Ok(())
    }
}

pub struct EventHandler {
    mq_client_set: Arc<mq::client::ClientSet>,
}

impl EventHandler {
    pub fn new(mq_client_set: Arc<mq::client::ClientSet>) -> Self {
        Self { mq_client_set }
    }
    pub fn spawn_handle_setup_event(
        &self,
        node_id: &str,
        payload: dto::event::endpoint::peer::wireguard_config::SetupEventPayload,
    ) {
        let mq_client_set = self.mq_client_set.clone();
        let node_id = node_id.to_string();
        tokio::spawn(async move {
            if let Err(e) = mq_client_set
                .node
                .endpoint
                .peer
                .wireguard_config
                .publish_setup_event(node_id.as_str(), payload)
                .await
            {
                log::error!("{:?}", e);
            }
        });
    }
}
